#!/usr/bin/python

# -*- coding: utf-8 -*-

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or (at
#  your option) any later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program; if not, write to the Free Software Foundation, Inc.,
#  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


import sys
import math
from qt import *
from qwt import *
from Numeric import *
import arrayfns
import time
 
#
# in plotpart.py there are all classes which are used to plot the data
# this class uses pyqwt to plot the data
# 
# Classes: 	QwtPlotImage:	is an example from the pyqwt-package, only little modifications
#		QwtImagePlot:	is an example from the pyqwt-package, only little modifications
#		measurePlot:	the functions defined in here are called from outside
#


# from scipy.pilutil
def bytescale(data, cmin=None, cmax=None, high=255, low=0):
	cmin=None
	cmax=None
	high=0
	low=0
	if data.typecode == UInt8:
        	return data
	high = high - low
	if cmin is None:
        	cmin = min(ravel(data))
	if cmax is None:
        	cmax = max(ravel(data))
    	scale = high *1.0 / (cmax-cmin or 1)
    	bytedata = ((data*1.0-cmin)*scale + 0.4999).astype(UInt8)
    	return bytedata + asarray(low).astype(UInt8)

# bytescale()

def linearX(nx, ny):
    	return repeat(arange(nx, typecode = Float32)[:, NewAxis], ny, -1)

# linearX()

def linearY(nx, ny):
    	return repeat(arange(ny, typecode = Float32)[NewAxis, :], nx, 0)

# linearY()

def square(n, min, max):
    	x = arrayfns.span(min, max, n, n)
    	y = transpose(x)
    	return sin(x)*cos(y)

# square()

def rectangle(nx, ny, scale):
    	# swap axes in the fromfunction call
    	s = scale/(nx+ny)
    	x0 = nx/2
    	y0 = ny/2
    
    	def test(y, x):
        	return cos(s*(x-x0))*sin(s*(y-y0))

    	result = fromfunction(test, (ny, nx))
    	return result

# rectangle()


class QwtPlotImage(QwtPlotMappedItem):

	def __init__(self, parent):
        	QwtPlotItem.__init__(self, parent)
        	self.xyzs = None
        	self.plot = parent

    # __init__()
    
   	def setData(self, xyzs, xScale = None, yScale = None):
        	self.xyzs = xyzs
	        shape = xyzs.shape
        	if xScale:
            		self.xMap = QwtDiMap(0, shape[0], xScale[0], xScale[1])
	            	self.plot.setAxisScale(QwtPlot.xBottom, *xScale)
        	else:
	        	self.xMap = QwtDiMap(0, shape[0], 0, shape[0])
       			self.plot.setAxisScale(QwtPlot.xBottom, 0, 	shape[0])
        	if yScale:
			self.yMap = QwtDiMap(0, shape[1], yScale[0], yScale[1])
			self.plot.setAxisScale(QwtPlot.yLeft, *yScale)
	        else:
			self.yMap = QwtDiMap(0, shape[1], 0, shape[1])
			self.plot.setAxisScale(QwtPlot.yLeft, 0, shape[1])
        	self.image = toQImage(bytescale(self.xyzs)).mirror(0, 1)
	        for i in range(0, 256):
        	    self.image.setColor(i, qRgb(i, 0, 255-i))

    	# setData()    

	def drawImage(self, painter, xMap, yMap):
        	"""Paint image to zooming to xMap, yMap

	        Calculate (x1, y1, x2, y2) so that it contains at least 1 pixel,
        	and copy the visible region to scale it to the canvas.
        	"""
        	# calculate y1, y2
        	# the scanline order (index y) is inverted with respect to the y-axis
        	y1 = y2 = self.image.height()
        	y1 *= (self.yMap.d2() - yMap.d2())
        	y1 /= (self.yMap.d2() - self.yMap.d1())
        	y1 = max(0, int(y1-0.5))
        	y2 *= (self.yMap.d2() - yMap.d1())
        	y2 /= (self.yMap.d2() - self.yMap.d1())
        	y2 = min(self.image.height(), int(y2+0.5))
        	# calculate x1, x2 -- the pixel order (index x) is normal
        	x1 = x2 = self.image.width()
        	x1 *= (xMap.d1() - self.xMap.d1())
        	x1 /= (self.xMap.d2() - self.xMap.d1())
        	x1 = max(0, int(x1-0.5))
        	x2 *= (xMap.d2() - self.xMap.d1())
        	x2 /= (self.xMap.d2() - self.xMap.d1())
        	x2 = min(self.image.width(), int(x2+0.5))
        	# copy
        	image = self.image.copy(x1, y1, x2-x1, y2-y1)
        	# zoom
        	image = image.smoothScale(xMap.i2()-xMap.i1()+1, yMap.i1()-yMap.i2()+1)
        	# draw
        	painter.drawImage(xMap.i1(), yMap.i2(), image)

    	# drawImage()

# class QwtPlotImage
    

class QwtImagePlot(QwtPlot):

	def __init__(self, *args):
        	QwtPlot.__init__(self, *args)
		# make a QwtPlot widget
		self.dataX = []
		self.dataY = []
		self.dataXCurr = []
		self.dataYCurr = []		
		self.olddataX = []
		self.olddataY = []
		self.keepdataX = []
		self.keepdataY = []
		self.dataY2 = []
		self.xpos=0
		self.ypos=0
		self.autoscale=True
		self.y2autoshow=True
		#self.dataX2 = []
		#self.dataY2 = []
		#self.olddataX2 = []
		#self.olddataY2 = []	
	
	        self.plotLayout().setMargin(0)
        	self.plotLayout().setCanvasMargin(0)
	        self.plotLayout().setAlignCanvasToScales(1)
		#self.setTitle('Measurement plot')
	        self.setAutoLegend(1)
        	self.setLegendPos(Qwt.Right)
		# set axis titles
		#self.setAxisTitle(QwtPlot.xBottom, 'Spannung (V)')
		#self.setAxisTitle(QwtPlot.yLeft, 'Strom (A)')
		# insert a few curves
		#cSin = self.insertCurve('y = pi*sin(x)')
		#cCos = self.insertCurve('y = 4*pi*sin(x)*cos(x)**2')
		coldMeasureCurve = self.insertCurve('previous\nmeasurement')
		cMeasureCurve = self.insertCurve('current\nmeasurement')
		ckeepMeasureCurve = self.insertCurve('kept\nmeasurement')
		cy2MeasureCurve = self.insertCurve('y2 measurement')
		self.cPointMarker = self.insertMarker('',0,0)
		self.setMarkerSymbol(self.cPointMarker,QwtSymbol(QwtSymbol.Cross, QBrush(Qt.red), QPen(Qt.red), QSize(15,15)))

		#cold2MeasureCurve = self.insertCurve('2 vorherige \nMessung', QwtPlot.xTop,QwtPlot.yRight)
		#c2MeasureCurve = self.insertCurve('2 aktuelle\nMessung',QwtPlot.xTop,QwtPlot.yRight)	
		# set curve styles
		#self.setCurvePen(cSin, QPen(Qt.green, 2))
		#self.setCurvePen(cCos, QPen(Qt.black, 2))
		self.setCurvePen(cMeasureCurve, QPen(Qt.green, 1))
		self.setCurvePen(coldMeasureCurve, QPen(Qt.gray, 1))
		self.setCurvePen(ckeepMeasureCurve, QPen(Qt.yellow, 1))
		self.setCurvePen(cy2MeasureCurve, QPen(Qt.black, 1))
		
		#self.setCurvePen(c2MeasureCurve, QPen(Qt.yellow, 2))
		#self.setCurvePen(cold2MeasureCurve, QPen(Qt.gray, 2))	
		# calculate 3 NumPy arrays
	        #x = arrayrange(-2*pi, 2*pi, 0.01)
	        #y = pi*sin(x)
        	#z = 4*pi*cos(x)*cos(x)*sin(x)
		# copy the data
		#self.setCurveData(cSin, x, y)
		#self.setCurveData(cCos, x, z)
		self.setCurveData(coldMeasureCurve, self.olddataX, self.olddataY)	
		self.setCurveData(cMeasureCurve, self.dataX, self.dataY)	
		self.setCurveData(ckeepMeasureCurve, self.keepdataX, self.keepdataY)
		self.setCurveData(cy2MeasureCurve, self.dataX, self.dataY2)
		self.enableAxis(QwtPlot.yRight)
		self.setCurveYAxis(cy2MeasureCurve, QwtPlot.yRight)
		#self.setCurveData(cold2MeasureCurve, self.olddataX2, self.olddataY2)	
		#self.setCurveData(c2MeasureCurve, self.dataX2, self.dataY2)		
		# insert a horizontal marker at y = 0
		mY = self.insertLineMarker('y = 0', QwtPlot.yLeft)
		self.setMarkerYPos(mY, 0.0)
		# insert a vertical marker at x = pi
		#mX = self.insertLineMarker('x = pi', QwtPlot.xBottom)
		#self.setMarkerXPos(mX, pi)
	        # image
	        self.plotImage = QwtPlotImage(self)
	        self.plotImage.setData(bytescale(linearX(512, 512)+linearY(512, 512)))
                self.setCanvasBackground(Qt.blue)
	        #self.plotImage.setData(
	        #    square(512,-2*pi, 2*pi), (-2*pi, 2*pi), (-2*pi, 2*pi))

        	self.zoomStack = []
        	self.connect(self,SIGNAL('plotMouseMoved(const QMouseEvent&)'),self.onMouseMoved)
        	self.connect(self,SIGNAL('plotMousePressed(const QMouseEvent&)'),self.onMousePressed)
        	self.connect(self,SIGNAL('plotMouseReleased(const QMouseEvent&)'),self.onMouseReleased)
        	self.connect(self, SIGNAL("legendClicked(long)"), self.toggleCurve)
        
        	# replot
        	self.replot()

    	# __init__()

	def drawCanvasItems(self, painter, rectangle, maps, filter):
        	self.plotImage.drawImage(painter, maps[QwtPlot.xBottom], maps[QwtPlot.yLeft])
        	QwtPlot.drawCanvasItems(self, painter, rectangle, maps, filter)

    	# drawCanvasItems()

    	def onMouseMoved(self, e):
        	pass

    # onMouseMoved()

    	def onMousePressed(self, e):
		self.autoscale=False
		self.emit(PYSIGNAL('autoscalechanged'),(0,))
        	if Qt.LeftButton == e.button():
            		# Python semantics: self.pos = e.pos() does not work; force a copy
	   		self.xpos = e.pos().x()
    			self.ypos = e.pos().y()
	          	self.enableOutline(1)
            		self.setOutlinePen(QPen(Qt.black))
            		self.setOutlineStyle(Qwt.Rect)
            		self.zooming = 1
            		if self.zoomStack == []:
                		self.zoomState = (self.axisScale(QwtPlot.xBottom).lBound(),self.axisScale(QwtPlot.xBottom).hBound(),self.axisScale(QwtPlot.yLeft).lBound(),self.axisScale(QwtPlot.yLeft).hBound(),self.axisScale(QwtPlot.yRight).lBound(),self.axisScale(QwtPlot.yRight).hBound(),)
        	elif Qt.RightButton == e.button():
            		self.zooming = 0
        	# fake a mouse move to show the cursor position
        	self.onMouseMoved(e)

    	# onMousePressed()

    	def onMouseReleased(self, e):
		if Qt.LeftButton == e.button():
        		xmin = min(self.xpos, e.pos().x())
            		xmax = max(self.xpos, e.pos().x())
            		ymin = min(self.ypos, e.pos().y())
            		ymax = max(self.ypos, e.pos().y())
            		self.setOutlineStyle(Qwt.Cross)
             		ymin2 = self.invTransform(QwtPlot.yRight, ymin)
            		ymax2 = self.invTransform(QwtPlot.yRight, ymax)           		
            		xmin = self.invTransform(QwtPlot.xBottom, xmin)
            		xmax = self.invTransform(QwtPlot.xBottom, xmax)
            		ymin = self.invTransform(QwtPlot.yLeft, ymin)
            		ymax = self.invTransform(QwtPlot.yLeft, ymax)

            		if xmin == xmax or ymin == ymax or ymin2 == ymax2:
                		return
            		self.zoomStack.append(self.zoomState)
            		self.zoomState = (xmin, xmax, ymin, ymax, ymin2, ymax2)
            		self.enableOutline(0)
        	elif Qt.RightButton == e.button():
            		if len(self.zoomStack):
                		xmin, xmax, ymin, ymax, ymin2, ymax2 = self.zoomStack.pop()
            		else:
				xdif=(self.axisScale(QwtPlot.xBottom).hBound()-self.axisScale(QwtPlot.xBottom).lBound())/2
				ydif=(self.axisScale(QwtPlot.yLeft).hBound()-self.axisScale(QwtPlot.yLeft).lBound())/2
				xmin=self.axisScale(QwtPlot.xBottom).lBound()-xdif
				xmax=self.axisScale(QwtPlot.xBottom).hBound()+xdif
				ymin=self.axisScale(QwtPlot.yLeft).lBound()-ydif
				ymax=self.axisScale(QwtPlot.yLeft).hBound()+ydif
				ydif2=(self.axisScale(QwtPlot.yRight).hBound()-self.axisScale(QwtPlot.yRight).lBound())/2
				ymin2=self.axisScale(QwtPlot.yRight).lBound()-ydif2
				ymax2=self.axisScale(QwtPlot.yRight).hBound()+ydif2
                		#return


        	self.setAxisScale(QwtPlot.xBottom, xmin, xmax)
        	self.setAxisScale(QwtPlot.yLeft, ymin, ymax)
        	self.setAxisScale(QwtPlot.yRight, ymin2, ymax2)
        	
        	self.replot()

    	# onMouseReleased()

    	def toggleCurve(self, key):
        	curve = self.curve(key)
        	if key==4: self.y2autoshow=False
        	if curve:
            		curve.setEnabled(not curve.enabled())
            		self.replot()

    	def newMessung(self,keepPlot=False):
		if keepPlot==True:
			self.keepdataX=self.olddataX
			self.keepdataY=self.olddataY
			self.setCurveData(3, self.keepdataX, self.keepdataY)
		self.olddataX=self.dataX
		self.olddataY=self.dataY
		#self.olddataX2=self.dataX2
		#self.olddataY2=self.dataY2
		self.dataX=[]
		self.dataY=[]
		self.dataXCurr=[]
		self.dataYCurr=[]
		self.dataY2=[]
		#self.dataX2=[]
		#self.dataY2=[]	
		self.setCurveData(1, self.olddataX, self.olddataY)
		self.setCurveData(2, self.dataX, self.dataY)
		self.setCurveData(4, self.dataX, self.dataY2)
		#self.setCurveData(3, self.olddataX2, self.olddataY2)
		#self.setCurveData(4, self.dataX2, self.dataY2)	
		self.replot()


    	def addMesspunkt(self,x,y,y2):
		self.dataX.append(x)
		self.dataY.append(y)
		self.dataY2.append(y2)

                #if manual pressed, then ignore
                if self.y2autoshow==True: 
        		b=False
                        if max(self.dataY2)!=0: b=True
                        curve = self.curve(4)
                        if curve: curve.setEnabled(b)
		#self.dataX2.append(x2)
		#self.dataY2.append(y2)
		self.setCurveData(2, self.dataX, self.dataY)
		self.setCurveData(4, self.dataX, self.dataY2)
		
		self.setMarkerPos(self.cPointMarker,x, y)
		#if len(self.zoomStack)==0:
		if self.autoscale:
			if len(self.dataX)>0:
				minScale=min(self.dataY)
				maxScale=max(self.dataY)
				min2Scale=min(self.dataY2)
				max2Scale=max(self.dataY2)				
				#if min(self.dataY2)<minScale: minScale=min(self.dataY2)
				#if max(self.dataY2)>maxScale: maxScale=max(self.dataY2)
				if minScale<0: minScale=minScale*1.1
				if minScale>0: minScale=minScale*0.9
				if maxScale>0: maxScale=maxScale*1.1
				if maxScale<0: maxScale=maxScale*0.9
				self.setAxisAutoScale(QwtPlot.yRight)				
		        	self.setAxisScale(QwtPlot.xBottom, min(self.dataX), max(self.dataX))
        			self.setAxisScale(QwtPlot.yLeft, minScale, maxScale)
				#self.setAxisScale(QwtPlot.yRight, min2Scale, max2Scale)
                                #self.setAxisAutoScale(QwtPlot.yLeft)
				#self.setAxisScale(QwtPlot.xTop, min(self.dataX2), max(self.dataX2))
           			#self.setAxisScale(QwtPlot.yRight, min(self.dataY2)*1.1, max(self.dataY2)*1.1)	
		
		#self.setCurveData(4, self.dataX2, self.dataY2)
		self.replot()

    	# toggleCurve()
	def moveMarkerDataNrBackward(self, n):
		i=len(self.dataX)-n
		if i>=0:
			self.setMarkerPos(self.cPointMarker,self.dataX[i], self.dataY[i])
			self.replot()
		

class measurePlot(QWidget):
	def __init__(self,parent):
        	QWidget.__init__(self,parent)
		selfLayout = QGridLayout(self,1,1,11,6,"measurePlot")
                self.groupBox = QGroupBox(self,"groupBox1")
		self.groupBox.setTitle("Measurement plot...")
        	self.groupBox.setColumnLayout(0,Qt.Vertical)
        	self.layout().setSpacing(3) #6)
        	self.layout().setMargin(8)
        	groupBoxLayout = QGridLayout(self.groupBox.layout())
        	groupBoxLayout.setAlignment(Qt.AlignTop)
		
		self.label1 = QLabel(self.groupBox,'label')
		self.plot = QwtImagePlot(self.groupBox)
		self.plot.setMinimumHeight( 150 )
		QToolTip.add( self.plot, "Plot of the current measurement (use left/right mousebutton to zoom)")
		
		self.autoscaleCheckBox=QCheckBox("autoscale plot",self.groupBox)
		self.autoscaleCheckBox.setChecked(True)
		self.connect(self.autoscaleCheckBox,SIGNAL('clicked()'),self.autoscale)
		self.connect(self.plot,PYSIGNAL('autoscalechanged'),self.autoscale)
		QToolTip.add( self.autoscaleCheckBox, "Enable or disable autoscaling of the plot")
				
		self.keepPlotCheckBox=QCheckBox("keep 'previous measurement'",self.groupBox)
		self.keepPlotCheckBox.setChecked(False)
		QToolTip.add( self.keepPlotCheckBox, "Also keep the measurement (which is the 'previous measurement' at hte moment")
				
		self.daten = QListBox(self.groupBox)
		QToolTip.add( self.daten, "Data of the current measurement")
		self.daten.setFixedHeight( self.daten.sizeHint().height() )
		self.connect( self.daten, SIGNAL("currentChanged(QListBoxItem *)"),self.dataClicked)
		
		groupBoxLayout.addMultiCellWidget(self.label1,0,0,0,1)
		groupBoxLayout.addMultiCellWidget(self.plot,1,1,0,1)
		groupBoxLayout.addWidget(self.autoscaleCheckBox,2,0)
		groupBoxLayout.addWidget(self.keepPlotCheckBox,2,1)
		groupBoxLayout.addMultiCellWidget(self.daten,3,3,0,1)
		self.timer = QTimer(self)
    		self.connect( self.timer, SIGNAL('timeout()'), self.timerEvent)
		
        	selfLayout.addWidget(self.groupBox,0,0)
		self.head=()
		self.messungsEnde=True
		
		
		
	def dataClicked(self, item):
		i=self.daten.index(item)
		n=self.daten.count()-i
		self.plot.moveMarkerDataNrBackward(n)
	
	def autoscale(self,auto=-1):
		if auto==-1:
			b=self.autoscaleCheckBox.isChecked()
			self.plot.autoscale=b
		elif auto==0:
			self.autoscaleCheckBox.setChecked(self.plot.autoscale)
		

	def newMessung(self,head):
		self.plot.newMessung(self.keepPlotCheckBox.isChecked())
		self.keepPlotCheckBox.setChecked(False)
		self.daten.clear()
		self.head=head
		for x in head:
			self.daten.insertItem(x)
		self.messungsEnde=False
		self.emit( PYSIGNAL("nextMessdaten"), (head,) )
			

	def newMessdaten(self,daten,datalist):
		i=0
		while i<len(daten):
			self.plot.addMesspunkt(float(datalist[i][0]),float(datalist[i][1]),float(datalist[i][2]))
			#print "newMessdaten"
			#print daten," ",x," ",y
			self.daten.insertItem(daten[i])
			i+=1
		self.daten.setCurrentItem(self.daten.count()-1)
		self.daten.ensureCurrentVisible()
		if self.messungsEnde==False: self.emit( PYSIGNAL("nextMessdaten"), (daten,) )
		
	def emitAllMessdaten(self):
		self.messungsEnde=True
		i=0
		zeilen=""
		while i<self.daten.count():
			zeilen+=unicode(self.daten.text(i))+'\n'
			i+=1
		self.emit( PYSIGNAL("emitAllMessdaten"), (zeilen,self.head) )
		
